package com.ggb.reactive.utils

import com.alibaba.fastjson.JSON;
import com.ggb.reactive.properties.WeChatProperties
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil
import org.apache.commons.lang3.RandomStringUtils
import org.apache.http.HttpHeaders
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.springframework.stereotype.Component
import java.io.File
import java.io.FileInputStream
import java.math.BigDecimal
import java.math.RoundingMode
import java.security.PrivateKey
import java.security.Signature
import java.security.cert.X509Certificate
import java.util.*
import com.alibaba.fastjson.JSONObject
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Component
class WeChatPayUtil(private val weChatProperties: WeChatProperties) {

        // 微信支付下单接口地址
    private val JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"

    // 申请退款接口地址
    private val REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"

    private val httpClient: CloseableHttpClient by lazy { getClient() }

    private fun getClient(): CloseableHttpClient {
        val merchantPrivateKey = PemUtil.loadPrivateKey(weChatProperties.privateKeyFilePath?.let {
            File(
                it
            )
        }?.let { FileInputStream(it) })
        val x509Certificate = PemUtil.loadCertificate(weChatProperties.weChatPayCertFilePath?.let {
            File(
                it
            )
        }?.let { FileInputStream(it) })
        val wechatPayCertificates = listOf(x509Certificate)

        val builder = WechatPayHttpClientBuilder.create()
            .withMerchant(weChatProperties.mchid, weChatProperties.mchSerialNo, merchantPrivateKey)
            .withWechatPay(wechatPayCertificates)

        return builder.build()
    }

    fun pay(orderNum: String, total: BigDecimal, description: String, openid: String): Mono<JSONObject> {
        return jsapi(orderNum, total, description, openid).flatMap { bodyAsString ->
            val jsonObject: JSONObject = JSON.parseObject(bodyAsString)
            val prepayId = jsonObject.getString("prepay_id") ?: return@flatMap Mono.just(jsonObject)

            val timeStamp = (System.currentTimeMillis() / 1000).toString()
            val nonceStr = RandomStringUtils.randomNumeric(32)
            val list: MutableList<Any> = mutableListOf()
            weChatProperties.appid?.let { list.add(it) }
            list.add(timeStamp)
            list.add(nonceStr)
            list.add("prepay_id=$prepayId")

            val message = list.joinToString("\n").toByteArray()

            val signature: Signature = Signature.getInstance("SHA256withRSA")
            signature.initSign(PemUtil.loadPrivateKey(weChatProperties.privateKeyFilePath?.let { File(it) }
                ?.let { FileInputStream(it) }))
            signature.update(message)
            val packageSign = Base64.getEncoder().encodeToString(signature.sign())

            val jo = JSONObject()
            jo["timeStamp"] = timeStamp
            jo["nonceStr"] = nonceStr
            jo["package"] = "prepay_id=$prepayId"
            jo["signType"] = "RSA"
            jo["paySign"] = packageSign

            Mono.just(jo)
        }
    }

    private fun jsapi(orderNum: String, total: BigDecimal, description: String, openid: String): Mono<String> {
        val jsonObject = JSONObject()
        jsonObject["appid"] = weChatProperties.appid
        jsonObject["mchid"] = weChatProperties.mchid
        jsonObject["description"] = description
        jsonObject["out_trade_no"] = orderNum
        jsonObject["notify_url"] = weChatProperties.notifyUrl

        val amount = JSONObject()
        amount["total"] = total.multiply(BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).toInt()

        amount["currency"] = "CNY"

        jsonObject["amount"] = amount

        val payer = JSONObject()
        payer["openid"] = openid

        jsonObject["payer"] = payer

        val body = jsonObject.toJSONString()
        return post(JSAPI, body)
    }

    private fun post(url: String, body: String): Mono<String> {
        return Mono.fromCallable {
            val httpPost = HttpPost(url)
            httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString())
            httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
            httpPost.addHeader("Wechatpay-Serial", weChatProperties.mchSerialNo)
            httpPost.entity = StringEntity(body, ContentType.APPLICATION_JSON)

            val response = httpClient.execute(httpPost)
            response.entity.content.bufferedReader().use { it.readText() }
        }
    }


}



//@Component
//class WeChatPayUtil(var weChatProperties: WeChatProperties) {
//
//    // 微信支付下单接口地址
//    private val JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
//
//    // 申请退款接口地址
//    private val REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"
//
//    /**
//     * 获取调用微信接口的客户端工具对象
//     */
//    private fun getClient(): CloseableHttpClient {
//        val merchantPrivateKey: PrivateKey = PemUtil.loadPrivateKey(weChatProperties.privateKeyFilePath?.let {
//            File(
//                it
//            )
//        }?.let { FileInputStream(it) })
//        val x509Certificate: X509Certificate = PemUtil.loadCertificate(weChatProperties.weChatPayCertFilePath?.let {
//            File(
//                it
//            )
//        }?.let { FileInputStream(it) })
//        val wechatPayCertificates: List<X509Certificate> = listOf(x509Certificate)
//
//        val builder: WechatPayHttpClientBuilder = WechatPayHttpClientBuilder.create()
//            .withMerchant(weChatProperties.mchid, weChatProperties.mchSerialNo, merchantPrivateKey)
//            .withWechatPay(wechatPayCertificates)
//
//        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
//        return builder.build()
//    }
//
//    /**
//     * 发送post方式请求
//     */
//    private fun post(url: String, body: String): String {
//        val httpClient: CloseableHttpClient = getClient()
//
//        val httpPost = HttpPost(url)
//        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString())
//        httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
//        httpPost.addHeader("Wechatpay-Serial", weChatProperties.mchSerialNo)
//        httpPost.entity = StringEntity(body, ContentType.APPLICATION_JSON)
//
//        val response = httpClient.execute(httpPost)
//        return response.entity.content.bufferedReader().use { it.readText() }
//    }
//
//    /**
//     * 发送get方式请求
//     */
//    private fun get(url: String): String {
//        val httpClient: CloseableHttpClient = getClient()
//
//        val httpGet = HttpGet(url)
//        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString())
//        httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
//        httpGet.addHeader("Wechatpay-Serial", weChatProperties.mchSerialNo)
//
//        val response = httpClient.execute(httpGet)
//        return response.entity.content.bufferedReader().use { it.readText() }
//    }
//
//    /**
//     * jsapi下单
//     */
//    private fun jsapi(orderNum: String, total: BigDecimal, description: String, openid: String): String {
//        val jsonObject = JSONObject()
//        jsonObject["appid"] = weChatProperties.appid
//        jsonObject["mchid"] = weChatProperties.mchid
//        jsonObject["description"] = description
//        jsonObject["out_trade_no"] = orderNum
//        jsonObject["notify_url"] = weChatProperties.notifyUrl
//
//        val amount = JSONObject()
//        amount["total"] = total.multiply(BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).toInt()
//
//        amount["currency"] = "CNY"
//
//        jsonObject["amount"] = amount
//
//        val payer = JSONObject()
//        payer["openid"] = openid
//
//        jsonObject["payer"] = payer
//
//        val body = jsonObject.toJSONString()
//        return post(JSAPI, body)
//    }
//
//    /**
//     * 小程序支付
//     */
//    fun pay(orderNum: String, total: BigDecimal, description: String, openid: String): JSONObject {
//        val bodyAsString = jsapi(orderNum, total, description, openid)
//        val jsonObject: JSONObject = JSON.parseObject(bodyAsString)
//
//        val prepayId = jsonObject.getString("prepay_id")
//        if (prepayId != null) {
//            val timeStamp = (System.currentTimeMillis() / 1000).toString()
//            val nonceStr = RandomStringUtils.randomNumeric(32)
//            val list: MutableList<Any> = ArrayList()
//            weChatProperties.appid?.let { list.add(it) }
//            list.add(timeStamp)
//            list.add(nonceStr)
//            list.add("prepay_id=$prepayId")
//
//            val stringBuilder = StringBuilder()
//            list.forEach { stringBuilder.append(it).append("\n") }
//            val message = stringBuilder.toString().toByteArray()
//
//            val signature: Signature = Signature.getInstance("SHA256withRSA")
//            signature.initSign(PemUtil.loadPrivateKey(weChatProperties.privateKeyFilePath?.let { File(it) }
//                ?.let { FileInputStream(it) }))
//            signature.update(message)
//            val packageSign = Base64.getEncoder().encodeToString(signature.sign())
//
//            val jo = JSONObject()
//            jo["timeStamp"] = timeStamp
//            jo["nonceStr"] = nonceStr
//            jo["package"] = "prepay_id=$prepayId"
//            jo["signType"] = "RSA"
//            jo["paySign"] = packageSign
//
//            return jo
//        }
//        return jsonObject
//    }
//
//    /**
//     * 申请退款
//     */
//    fun refund(outTradeNo: String, outRefundNo: String, refund: BigDecimal, total: BigDecimal): String {
//        val jsonObject = JSONObject()
//        jsonObject["out_trade_no"] = outTradeNo
//        jsonObject["out_refund_no"] = outRefundNo
//
//        val amount = JSONObject()
//        amount["total"] = total.multiply(BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).toInt()
//        amount["refund"] = refund.multiply(BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).toInt()
//
//        amount["currency"] = "CNY"
//
//        jsonObject["amount"] = amount
//        jsonObject["notify_url"] = weChatProperties.refundNotifyUrl
//
//        val body = jsonObject.toJSONString()
//
//        return post(REFUNDS, body)
//    }
//}